#include "SequenceSolver.hpp"

SequenceSolver::SequenceSolver(int _sol_type, int _pc_type, int _first):
        solver_type(_sol_type), prec_type(_pc_type), last_stepId(_first - 1)
{
    setProcessRealTime();
    if (solver_type >= 1000) {// 如果采用GPU求解器，打印一下当前的GPU设备信息
        init_GPU_query();
        init_GPU_coeff();
    }
    // 打印一下求解器类型和预条件类型
    assert( solver_type ==    0 || solver_type ==    1 || solver_type ==    2 ||
                                   solver_type ==   11 || solver_type ==   22 || // reorder
            solver_type == 1000 || solver_type == 1001 || solver_type == 1002 ||
                                   solver_type == 1011 || solver_type == 1022);
    printf("Solver  type: %d\n", solver_type);
    printf("Precond type: %d\n", prec_type);
    prev_hfs.clear();
    prev_hfs.push_back(0);
}

SequenceSolver::~SequenceSolver()
{
    if (solver_type >= 1000) {
        clear_GPU_coeff();
    }
    void* data_ptrs[4] = {last_mem, store_mem, device_last_A, device_store_A};
    for (int i = 0; i < sizeof(data_ptrs)/sizeof(void*); i++) {
        if (solver_type >= 1000) device_free(data_ptrs[i]);
        else free(data_ptrs[i]);
    }
}

void SequenceSolver::solve(const int step, const int nrows, double* A64, double* rhs64, TEST_TIME & record)
{
    if (step != last_stepId && step != last_stepId + 1) {
        printf("Error in solve: last step is %d, but current step is %d\n", last_stepId, step);
        exit(-1);
    }
    if (step == last_stepId) {// 仍然是同一时间步的多次测试
        // printf("Still do S%d\n", step);
        assert(store_nrows == nrows);
        if (solver_type >= 1000) device_free(store_mem);
        else free(store_mem);
        store_mem = nullptr;
    } else {// == last_stepId + 1 新的时间步
        // printf("To do a new S%d\n", step);
        if (solver_type >= 1000) {
            device_free(last_mem); last_mem = nullptr;
            device_free(device_last_A); device_last_A = device_store_A;// 清理上两步的内存，更新为上一步的
            device_store_A = nullptr;// 内存从store_A转移到last_A中
        } else {
            free(last_mem); last_mem = nullptr;
        }
        last_nrows = store_nrows;
        last_mem = store_mem;
        prev_hfs.push_back(nrows >> 1);
    }

    double* d_A = nullptr, * d_rhs = nullptr;
    if (solver_type >= 1000) {// Host -> Device
        if (device_last_A == nullptr) {// 说明当前是第一个时间步的求解，GPU上还没有上一步的矩阵
            // printf("Total copy\n");
            d_A = create_copy_to_device(A64, nrows * nrows, record);// 需要将整个A矩阵拷贝过去
            d_rhs = create_copy_to_device(rhs64, nrows, record);
        } else {// 已经有了上一时间步的矩阵，只需要传增量的部分矩阵过去
            // printf("Partial copy\n");
            if (solver_type > 1010) {// 需要重排
                create_reorder_copyPart_to_device(A64, rhs64, nrows, (double*) device_last_A, last_nrows, record, prev_hfs, d_A, d_rhs);
            } else {
                create_copyPart_to_device(A64, rhs64, nrows, (double*) device_last_A, last_nrows, record, d_A, d_rhs);
            }
        }
        assert(d_A); assert(d_rhs);
    }

    switch (solver_type)
    {
    case 0:
        direct_solver(nrows, A64, rhs64, record); break;
    case 1:
        store_mem = iterat_solver(nrows, A64, rhs64, prec_type, record, last_nrows, (float* ) last_mem); break;
    case 11:
        store_mem = iterat_reorder_solver(nrows, A64, rhs64, prec_type, record, last_nrows, (float* ) last_mem, prev_hfs); break;
    case 2:
        store_mem = direct_reuse_solver(nrows, A64, rhs64, record, last_nrows, (double*) last_mem); break;
    case 22:
        store_mem = direct_reuse_reorder_solver(nrows, A64, rhs64, record, last_nrows, (double*) last_mem, prev_hfs); break;

    case 1000:
        gpu_direct_solver(nrows, d_A, d_rhs, record); break;
    case 1001:
    case 1011:// 1011是重排后的，只能用91
        store_mem = gpu_iterat_solver(nrows, d_A, d_rhs, prec_type, record, last_nrows, (float*) last_mem, solver_type == 1011 && prec_type == 91); break;    
    case 1002:
    case 1022:
        store_mem = gpu_direct_reuse_solver(nrows, d_A, d_rhs, record, last_nrows, (double*) last_mem, solver_type == 1022); break;
    default: exit(-1);
    }
    store_nrows = nrows;

    if (solver_type >= 1000) {// Device -> Host
        copy_to_host(d_rhs, rhs64, nrows, record);
        if (device_store_A) device_free(device_store_A);
        device_store_A = d_A; d_A = nullptr;// 避免内存泄漏
        device_free(d_rhs); d_rhs = nullptr;
    }
    // 记录一下
    last_stepId = step;
}